import {ref, watch} from 'vue'
import {ModifiedMethod} from "../types";

export interface UseValueModel<T> {
    // 字段名
    property: string
    // 默认值（单向输入，需要通过 change 事件侦听变化）
    value?: T
    // 行级数据
    row: Record<string, any>
    // 数据变化事件
    change?: ModifiedMethod
}


/**
 * 表格 - 数据编辑结果
 */
export interface Modified {
    /**
     * 修改之后的值
     */
    value: any;
    /**
     * 修改的字段名
     */
    field: string;
    /**
     * 预留数据 ID，如果存在的话
     */
    id: unknown;
    /**
     * 预留原始数据对象实体，如果存在的话
     */
    ref: unknown;
}


/**
 * 表格 - 编辑即时保存的回调函数
 *
 * 完成单元格数据编辑之后，触发当前函数
 */
export type ModifiedMethod<T> = (evt: string, row: T, arg: Modified) => void;


/**
 * 值模型
 *
 * @param props 组件入参
 */
export function useValueModel<T>(props: UseValueModel<T>) {

    // 定义要触发的事件，用于更新 v-model 的值
    const valueModel: Ref<T> = ref<T>(props.value);

    /**
     * 监听父组件传递的 value 变化
     *
     * 组件拆成两份封装，主要就是为了这个侦听，暂时没找到侦听 slot 传值的方法，只能先这么写了
     */
    watch(() => props.value, (val) => {
        valueModel.value = val;
    }, {immediate: true});


    /**
     * 值改变事件
     *
     * @param val 值
     */
    const onchange = (val: T): void => {
        try {
            // 返回 row 对象，修改成功，直接修改 row 的字段，而不需要刷新整个表格
            const arg: Modified = {field: props.property, value: val};
            props.change('$edit', props.row, arg);
        } catch (e) {
            // rollback
            console.error(e);
            valueModel.value = props.value;
        }
    }

    return {valueModel, onchange}
}

/**
 * 获取行级数据中的校验属性
 * @param props 组件入参
 */
export function useValidator(props: UseValueModel<T>) {
    const validation: Ref<Validation> = ref(undefined);

    // 必须在数据加载前创建 $validators 对象，后续不再做数据侦听
    if (props.row.$validators) {
        const $valid = props.row.$validators;
        validation.value = $valid[props.property];
    }

    return {validation}
}